package cn.jolyoulu.common.poi.excel.reader.sax;

import cn.jolyoulu.common.poi.excel.reader.handler.oleobject.AbsOleObjectReaderHandler;
import cn.jolyoulu.common.poi.excel.reader.handler.sheet.AbsSheetReaderHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.opc.*;
import org.apache.poi.util.XMLHelper;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import cn.jolyoulu.common.poi.excel.reader.handler.drawing.AbsDrawingReaderHandler;
import cn.jolyoulu.common.poi.excel.reader.handler.sheet.DefSheetReaderHandler;
import cn.jolyoulu.common.poi.excel.reader.handler.sheet.SliceSheetReaderHandler;

import java.io.*;
import java.util.*;
import java.util.function.Consumer;

/**
 * @Author: JolyouLu
 * @Date: 2023/1/20 15:11
 * @Description Excel读取工具
 */
@Slf4j
public class SaxExcelReader<T> {

    protected static final int DEFAULT_SHEET_INDEX = 0;
    /**
     * 当前读取的sheet地址
     */
    protected Integer sheetIndex = DEFAULT_SHEET_INDEX;

    private AbsSheetReaderHandler<T> sheetHandler;


    private SaxExcelReader(Class<T> dataType) {
        this.sheetHandler = new DefSheetReaderHandler<T>(dataType);
    }

    /**
     * 初始化一个DefaultExcelReader
     *
     * @param dataType 需要识别的类
     * @return SaxExcelReader
     */
    public static <T> SaxExcelReader<T> of(Class<T> dataType) {
        return new SaxExcelReader<>(dataType);
    }

    /**
     * 设置需要读取的sheet
     *
     * @param index sheet的下标，1开始
     * @return SaxExcelReader
     */
    public SaxExcelReader<T> sheet(int index) {
        this.sheetIndex = index;
        this.sheetHandler.setStartRow(2);
        return this;
    }

    /**
     * 设置需要读取的sheet，与开始读取的行号
     *
     * @param index    sheet的下标，0开始
     * @param startRow 开始读取的行号，1开始
     * @return SaxExcelReader
     */
    public SaxExcelReader<T> sheet(int index, int startRow) {
        this.sheetIndex = index;
        this.sheetHandler.setStartRow(startRow);
        return this;
    }

    /**
     * 简单读取，读取Excel中数据
     *
     * @param inputStream excel文件流
     *                    inputStream流使用完毕后需要自己手动关闭
     */
    public List<T> read(InputStream inputStream, AbsSheetReaderHandler<T> handler) {
        //解压Sax
        try (OPCPackage pkg = OPCPackage.open(inputStream)) {
            //开始执行读取
            XSSFReader xssfReader = new XSSFReader(pkg);
            XMLReader parser = XMLHelper.newXMLReader();
            parser.setContentHandler(new SaxSheetReaderHandler(xssfReader.getSharedStringsTable(), handler));
            try (InputStream sheet = xssfReader.getSheet("rId" + (sheetIndex + 1))) {
                InputSource sheetSource = new InputSource(sheet);
                parser.parse(sheetSource);
            }
        } catch (Exception e) {
            log.error("SaxExcelReader读取数据失败", e);
        }
        return handler.getData();
    }

    /**
     * 简单读取，读取Excel中数据
     *
     * @param inputStream excel文件流
     *                    inputStream流使用完毕后需要自己手动关闭
     */
    public List<T> read(InputStream inputStream) {
        return read(inputStream, this.sheetHandler);
    }

    /**
     * 简单读取，读取Excel中数据
     *
     * @param file 文件
     */
    public List<T> read(File file) {
        try (FileInputStream inputStream = new FileInputStream(file)) {
            return read(inputStream);
        } catch (Exception e) {
            log.error("SaxExcelReader读取数据失败", e);
        }
        return sheetHandler.getData();
    }

    /**
     * 分配读取，读取Excel中数据
     *
     * @param file 文件
     */
    public void read(File file, Integer size, Consumer<List<T>> consumer) {
        try (FileInputStream inputStream = new FileInputStream(file)) {
            List<T> residueList = read(inputStream, new SliceSheetReaderHandler<T>(this.sheetHandler, size, consumer));
            consumer.accept(residueList); //这里是吧剩余的数据都处理了
        } catch (Exception e) {
            log.error("SaxExcelReader读取数据失败", e);
        }
    }

    /**
     * 获取excel中的图片
     *
     * @param inputStream excel文件流
     *                    inputStream流使用完毕后需要自己手动关闭
     */
    public void readImages(InputStream inputStream, AbsDrawingReaderHandler handler) {
        //解压Sax
        try (OPCPackage pkg = OPCPackage.open(inputStream)) {
            PackagePart drawingPkg = pkg.getPart(PackagingURIHelper.createPartName("/xl/drawings/drawing" + (this.sheetIndex + 1) + ".xml"));
            XMLReader parser = XMLHelper.newXMLReader();
            parser.setContentHandler(new SaxDrawingReaderHandler(pkg, drawingPkg, handler));
            try (InputStream drawing = drawingPkg.getInputStream()) {
                InputSource drawingSource = new InputSource(drawing);
                parser.parse(drawingSource);
            }
        } catch (Exception e) {
            log.error("SaxExcelReader读取数据失败", e);
        }
    }


    /**
     * 简单读取，读取Excel中数据
     *
     * @param file 文件
     */
    public void readImages(File file, AbsDrawingReaderHandler handler) {
        try (FileInputStream inputStream = new FileInputStream(file)) {
            readImages(inputStream, handler);
        } catch (Exception e) {
            log.error("SaxExcelReader读取数据失败", e);
        }
    }

    /**
     * 获取excel中的附件
     *
     * @param inputStream excel文件流
     *                    inputStream流使用完毕后需要自己手动关闭
     */
    public void readOleObject(InputStream inputStream, AbsOleObjectReaderHandler handler) {
        //解压Sax
        try (OPCPackage pkg = OPCPackage.open(inputStream)) {
            PackagePart sheetPkg = pkg.getPart(PackagingURIHelper.createPartName("/xl/worksheets/sheet" + (this.sheetIndex + 1) + ".xml"));
            XMLReader parser = XMLHelper.newXMLReader();
            parser.setContentHandler(new SaxOleObjectReaderHandler(pkg, sheetPkg, handler));
            try (InputStream drawing = sheetPkg.getInputStream()) {
                InputSource drawingSource = new InputSource(drawing);
                parser.parse(drawingSource);
            }
        } catch (Exception e) {
            log.error("SaxExcelReader读取数据失败", e);
        }
    }

    /**
     * 简单读取，读取Excel中数据
     *
     * @param file 文件
     */
    public void readOleObject(File file, AbsOleObjectReaderHandler handler) {
        try (FileInputStream inputStream = new FileInputStream(file)) {
            readOleObject(inputStream, handler);
        } catch (Exception e) {
            log.error("SaxExcelReader读取数据失败", e);
        }
    }

}
